//
// Created by jm on 2022/1/12.
//
#include "zlogger/asynctimer.h"
int AsyncLogger::Initialize(const std::string &str) {
  stTelemetry_Conf l_stConf = {};
  P7_Set_Crash_Handler();
  //create P7 client object
  p_client_ = P7_Create_Client(TM(("/P7.Sink=Auto /P7.Addr=" + str).c_str()));
  if (NULL == p_client_) {
    goto l_lblExit;
  }
  l_stConf.pContext = nullptr;
  l_stConf.pTimestamp_Callback = nullptr;
  l_stConf.qwTimestamp_Frequency = 0ull;
  //create P7 telemetry object
  p_telemetry_ = P7_Create_Telemetry(p_client_, TM("Timer"), &l_stConf);
  if (nullptr == p_telemetry_) {
    goto l_lblExit;
  }
  init_flg = true;
  return 0;
  l_lblExit:
  if (p_telemetry_) {
    p_telemetry_->Release();
    p_telemetry_ = nullptr;
  }

  if (p_trace_) {
    p_trace_->Release();
    p_trace_ = nullptr;
  }

  if (p_client_) {
    p_client_->Release();
    p_client_ = nullptr;
  }

  return -1;
}
uint64_t AsyncLogger::TimerStart(const std::string &name, uint32_t index) {
  if (!init_flg) {
    return -1;
  }
  uint64_t id = 0;
  std::lock_guard<std::mutex> lock(mtx_);
  auto name_iter = timer_name_id_map_.find(name);
  if (name_iter == timer_name_id_map_.end()) {
    id = timer_name_id_map_.size();
    timer_name_id_map_.insert(std::make_pair(name, id));
  } else {
    id = name_iter->second;
  }
  uint64_t timer_index = id << 32 | index;
  auto iter_send = timer_id_sendid_map_.find(id);
  if (iter_send == timer_id_sendid_map_.end()) {
    uint16_t send_id;
    std::string topic = "FunctionTimer/" + name + " (ms)";
    p_telemetry_->Create(TM(topic.c_str()), 0, 0, 100, 100, 1, &send_id);
    timer_id_sendid_map_.insert(std::make_pair(id, send_id));
    //std::cout << "send_id created " << send_id << std::endl;
  }
  auto timestamp =
      std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
  timer_id_starttime_map_.insert(std::make_pair(timer_index, timestamp));
  return timestamp;
}
AsyncLogger *AsyncLogger::GetInstance() {
  static auto *instance = new AsyncLogger();
  return instance;
}
uint64_t AsyncLogger::TimerEnd(const std::string &name, uint32_t index) {
  uint64_t during;
  if (!init_flg) {
    return -1;
  }
  auto timestamp =
      std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();

  std::lock_guard<std::mutex> lock(mtx_);
  uint64_t id = 0;
  auto name_iter = timer_name_id_map_.find(name);
  if (name_iter == timer_name_id_map_.end()) {
    return timestamp;
  } else {
    id = name_iter->second;
  }
  auto iter = timer_id_starttime_map_.find(id << 32 | index);
  if (iter == timer_id_starttime_map_.end()) {
    return timestamp;
  } else {
    during = timestamp - iter->second;
  }
  auto send_iter = timer_id_sendid_map_.find(id);
  if (send_iter != timer_id_sendid_map_.end()) {
    p_telemetry_->Add(send_iter->second, ((double) during / 1000000));
  }
  return timestamp;
}
void AsyncLogger::Clear() {
  std::lock_guard<std::mutex> lock(mtx_);
  timer_id_starttime_map_.clear();
  //time_statistics.clear();
}
int AsyncLogger::AddVariable(const std::string &name, double variable) {
  uint16_t send_id;
  auto name_iter = variable_name_sendid_map_.find(name);
  if (name_iter == variable_name_sendid_map_.end()) {
    p_telemetry_->Create(TM(("Variable/" + name).c_str()), 0, 0, 100, 100, 1, &send_id);
    variable_name_sendid_map_.insert(std::make_pair(name, send_id));
  } else {
    send_id = name_iter->second;
  }

  if (p_telemetry_->Add(send_id, variable)) {
    return 1;
  } else {
    return 0;
  }
}
int AsyncLogger::AddCount(const std::string &name) {
  uint16_t send_id;
  uint32_t count;
  auto name_iter = count_name_sendid_map_.find(name);
  if (name_iter == count_name_sendid_map_.end()) {

    p_telemetry_->Create(TM(("Count/" + name).c_str()), 0, 0, 100000, 100000, 1, &send_id);
    count_name_sendid_map_.insert(std::make_pair(name, send_id));
    count = 1;
    count_name_number_map_.insert(std::make_pair(name, count));
  } else {
    send_id = name_iter->second;
    auto count_iter = count_name_number_map_.find(name);
    count_iter->second = count_iter->second + 1;
    count = (count_iter->second);
  }

  if (p_telemetry_->Add(send_id, count)) {
    return 1;
  } else {
    return 0;
  }
}
Timer::Timer(const std::string &name) : name_(name) {
  {
    std::lock_guard<std::mutex> lock(timer_mtx);
    index = timer_count++;
  }
  AsyncLogger::GetInstance()->TimerStart(name_, index);
}
Timer::~Timer() {
//    std::lock_guard<std::mutex> lock(timer_mtx);
  AsyncLogger::GetInstance()->TimerEnd(name_, index);
}
std::string GetPathOrURLShortName(std::string strFullName) {
  if (strFullName.empty()) {
    return "";
  }

  string_replace(strFullName, "/", "\\");

  std::string::size_type iPos = strFullName.find_last_of('\\') + 1;

  return strFullName.substr(iPos, strFullName.length() - iPos);
}
void string_replace(std::string &strBig, const std::string &strsrc, const std::string &strdst) {
  std::string::size_type pos = 0;
  std::string::size_type srclen = strsrc.size();
  std::string::size_type dstlen = strdst.size();

  while ((pos = strBig.find(strsrc, pos)) != std::string::npos) {
    strBig.replace(pos, srclen, strdst);
    pos += dstlen;
  }
}
